package main

import (
    "github.com/gin-gonic/gin"
    "gorm.io/driver/mysql"
    "gorm.io/gorm"
	"math/rand"
    "net/http"
    "strconv"
    "time"
)

// 生成20位随机字符串作为token
func generateToken() string {
	str := "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
	bytes := []byte(str)
	result := []byte{}
	rand.Seed(time.Now().UnixNano() + int64(rand.Intn(100)))
	for i := 0; i < 20; i++ {
		result = append(result, bytes[rand.Intn(len(bytes))])
	}
	return string(result)
}

// 生成随机14位银行卡号
func generateCardNumber() string {
    prefix := "622202"
    rand.Seed(time.Now().UnixNano())
    suffix := strconv.Itoa(rand.Intn(99999999-10000000+1) + 10000000)
    return prefix + suffix
}

// 生成随机六位密码
func generateCardPassword() string {
    rand.Seed(time.Now().UnixNano())
    return strconv.Itoa(rand.Intn(999999-100000+1) + 100000)
}

// account表
type Account struct {
    AccountID  int       `gorm:"column:account_id;primary_key;auto_increment"`
    CardNumber string    `gorm:"column:card_number;unique;not null"`
    Password   string    `gorm:"column:password;not null"`
    Balance    float64   `gorm:"column:balance;not null;check:balance >= 0"`
    IsAdmin    bool      `gorm:"column:is_admin;not null;default:false;check:is_admin IN (0,1)"`
}

// 解决浏览器跨域
func Cors() gin.HandlerFunc {
	return func(context *gin.Context) {
		method := context.Request.Method

		context.Header("Access-Control-Allow-Origin", "*")
		context.Header("Access-Control-Allow-Headers", "Content-Type,AccessToken,X-CSRF-Token, Authorization, Token, x-token")
		context.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PATCH, PUT")
		context.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type")
		context.Header("Access-Control-Allow-Credentials", "true")

		if method == "OPTIONS" {
			context.AbortWithStatus(http.StatusNoContent)
		}
	}
}

func main() {

    // 连接数据库
    dsn := "[your db's dsn]"
    db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
    if err != nil {
        panic("failed to connect database")
    }

	// 自动迁移数据库
	db.AutoMigrate(&Account{})

    // 创建一个默认的Gin引擎
    r := gin.Default()

    // 处理跨域
    r.Use(Cors())
    
	// TODO: 此处是模拟redis 存tokens，可以改成真正的jwt token（使用中间件）
	tokens := make(map[string]string)

    // 登陆接口
    r.POST("/login", func(c *gin.Context) {

		var account Account
		username := c.PostForm("username")
        password := c.PostForm("password")
		result := db.Where("card_number = ?", username).First(&account)

        // TODO: 错误处理

        if result.RowsAffected == 1 && account.Password == password {

			// 生成随机token 并记录token到卡号的映射
			var token string
			for true {
				token = generateToken()
				_, ok := tokens[token]
				// token不能和原有的token重复
				if !ok {
					break
				}
			}
			tokens[token] = username

            c.JSON(200, gin.H{"message": "登录成功", "status": "0", "token": token})
        } else {
            c.JSON(200, gin.H{"message": "用户名或密码错误", "status": "1"})
        }
    })

	// 开户接口
    r.POST("/open", func(c *gin.Context) {
        // TODO: 以下四个操作都要首先验证token 冗余代码很多 如何提高代码复用度
        // 验证操作者的权限
		var account Account
        token := c.PostForm("token")
        card_number, ok := tokens[token]

        // token无效或失效
        if !ok {
            c.JSON(200, gin.H{"message": "无效的token", "status": "1"})
            return
        }


		result := db.Where("card_number = ?", card_number).First(&account)
        
        // 权限合格 可以为其开户
        if result.RowsAffected == 1 && account.IsAdmin {
            // 随机生成合适的卡号和密码
            remake:
            account = Account{}
            new_card_number := generateCardNumber()
            result = db.Where("card_number = ?", new_card_number).First(&account)
            if result.RowsAffected == 1 {
                goto remake
            }
            // TODO: 错误处理

            new_passwd := generateCardPassword()
            new_account := Account{CardNumber: new_card_number, Password: new_passwd, Balance: 0, IsAdmin: false}
            result = db.Create(&new_account)

            if result.RowsAffected == 1 {
                c.JSON(200, gin.H{"message": "开户成功", "status": "0", "card_number": new_card_number, "password": new_passwd})
                return
            } 

            // TODO: 错误处理

        } else {
            c.JSON(200, gin.H{"message": "无效的管理员用户", "status": "1"})
        }


    })

	// 删户接口
    r.POST("/delete", func(c *gin.Context) {
        // 验证操作者的权限
		var account Account
        token := c.PostForm("token")
        card_number, ok := tokens[token]

        // token无效或失效
        if !ok {
            c.JSON(200, gin.H{"message": "无效的token", "status": "1"})
            return
        }


		result := db.Where("card_number = ?", card_number).First(&account)
        
        // 权限合格 可以为其删户
        if result.RowsAffected == 1 && account.IsAdmin {

            account = Account{}
            result = db.Where("card_number = ?", c.PostForm("card_number")).Delete(&account)

            // TODO：按理来说不能允许一个管理员删除其他管理员的账户 此处该怎么改
            if result.RowsAffected == 1 {
                c.JSON(200, gin.H{"message": "删户成功", "status": "0"})
                return
            } 

            c.JSON(200, gin.H{"message": "删户失败，请检查卡号是否正确", "status": "1"})

        } else {
            c.JSON(200, gin.H{"message": "无效的管理员用户", "status": "1"})
        }

    })

	// 余额查询接口
    r.POST("/balance", func(c *gin.Context) {
        var account Account
        token := c.PostForm("token")
        card_number, ok := tokens[token]

        // token无效或失效
        if !ok {
            c.JSON(200, gin.H{"message": "无效的token", "status": "1"})
            return
        }

		result := db.Where("card_number = ?", card_number).First(&account)

        // 查
        if result.RowsAffected == 1 {
            c.JSON(200, gin.H{"message": "查询成功", "status": "0", "balance": strconv.FormatFloat(account.Balance, 'f', 2, 64)})
        } else {
            c.JSON(200, gin.H{"message": "查询失败，用户不存在", "status": "1"})
        }
    })

	// 转账接口
    r.POST("/transfer", func(c *gin.Context) {
        var account Account
        var to Account
        token := c.PostForm("token")
        to_account := c.PostForm("to")
        card_number, ok := tokens[token]
        // token无效或失效
        if !ok {
            c.JSON(200, gin.H{"message": "无效的token", "status": "1"})
            return
        }

        // 得到两个账户
		result  := db.Where("card_number = ?", card_number).First(&account)
        result2 := db.Where("card_number = ?", to_account).First(&to)

        // 如果任何一个账户不存在
        if result.RowsAffected != 1 || result2.RowsAffected != 1 {
            c.JSON(200, gin.H{"message": "转账失败，用户不存在!", "status": "1"})
            return 
        }

        amount, err := strconv.ParseFloat(c.PostForm("amount"), 64) 
        if err != nil {
            c.JSON(200, gin.H{"message": "转账失败，金额数目不合规范!", "status": "1"})
            return 
        }

        // 如果余额不足
        if account.Balance - amount < 0 {
            c.JSON(200, gin.H{"message": "转账失败，余额不足!", "status": "1"})
            return 
        }

        // 进行转账事务
        account.Balance -= amount
        to.Balance += amount

        db.Save(&account)
        db.Save(&to)

        c.JSON(200, gin.H{"message": "转账成功！", "status": "0"})
    })

    // 启动服务器，监听3008端口
    r.Run(":3008")
}